import webapp2
import wtforms
from wtforms.ext.appengine.db import model_form
from cStringIO import StringIO
import sys
import traceback
import models
import handlers
import new
import json
import logging
import tree
from google.appengine.api.users import is_current_user_admin, create_login_url, get_current_user

jinja_environment = handlers.jinja_environment

class PasteForm(model_form(models.Paste)):
    pass

def to_module(code, test_data=''):
    module = new.module('usercode')
    module.__dict__['test_input'] = test_data
    exec code in module.__dict__
    return module


class PasteViewHandler(webapp2.RequestHandler):
    def post(self):
        self.get()
    
    def get(self):
        paste_id = self.request.path
        if paste_id == '/':
            paste_id = '/home'
        
        paste = models.Paste.fetch(paste_id)
        if not paste:
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.set_status(404)
            self.response.out.write('404: NOT FOUND')
            return
        
        if paste.admin_only and not is_current_user_admin():
            if not get_current_user():
                self.redirect(create_login_url(self.request.url))
                return
            
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.set_status(401)
            self.response.out.write('401: UNAUTHORIZED')
            return
        
        if 'python' in paste.mimetype.lower():
            old_stdout = sys.stdout
            sys.stdout = out = StringIO()
            try:
                self.response.headers['Content-Type'] = 'text/plain'
                usercode = tree.get_module(paste_id)
                method = self.request.method.lower()
                if hasattr(usercode, method):
                    getattr(usercode, method)(self)
                else:
                    self.response.headers['Content-Type'] = 'text/plain'
                    self.response.out.write(out.getvalue())
            except:
                self.response.headers['Content-Type'] = 'text/plain'
                self.response.set_status(500)
                traceback.print_exc(file=self.response.out)
            finally:
                sys.stdout = old_stdout
            
        else:
            self.response.headers['Content-Type'] = str(paste.mimetype)
            self.response.out.write(paste.code)
        
class TreeDataHandler(webapp2.RequestHandler):
    def to_json(self, paste):
        children = list([self.to_json(child) for child in paste.children])
        children = sorted(children, key=lambda child: child['data'])
        return {
                "data": paste.name,
                "attr": {
                         "id": paste.key().id()
                         },
                "state": "open" if paste.open else "closed",
                "children": children
                }
    
    def get(self):
        id = int(self.request.get('id'))
        
        self.response.headers['Content-Type'] = 'text/json'
        
        if id and id != "0":
            parent = models.Paste.get_by_id(id)
        else:
            parent = None
            
        query = models.Paste.all()
        query.filter("parent_paste =", parent)
        children = list([self.to_json(child) for child in query])
        children = sorted(children, key=lambda child: child['data'])
        
        if parent:
            data = children
        else:
            data = {
                    "data": "/",
                    "attr": {
                             "id": 0
                             },
                    "state": "open",
                    "children": children
                    }
            
        self.response.out.write(json.dumps(data))
        
    def move_node(self, moved_id, new_parent_id, copy):
        node = models.Paste.get_by_id(moved_id)
        if new_parent_id == 0:
            parent = None
        else:
            parent = models.Paste.get_by_id(new_parent_id)
        
        node.parent_paste = parent
        node.put()
        
        return { "status": 1, "id": node.key().id() }
        
    def update_node(self, id, title, mimetype, restricted, text, open):
        node = models.Paste.get_by_id(id)
        if title:
            node.name = title
        if mimetype:
            node.mimetype = mimetype
        if restricted:
            node.admin_only = restricted == "1"
        if text:
            node.code = text
        if open:
            node.open = open == "1"
        node.put()
        
        return { "status": 1, "id": node.key().id() }
        
    def create_node(self, parent_id, title):
        node = models.Paste(name=title, mimetype="text/plain")
        node.code = ""
        node.admin_only = False
        if parent_id != 0:
            node.parent_paste = models.Paste.get_by_id(parent_id)
        node.put()
        return { "status": 1, "id": node.key().id() }
        
    def remove_node(self, id):
        node = models.Paste.get_by_id(id)
        if node.children.count() > 0:
            return { "status": 0, "error": "child nodes exist" }
        node.delete()
        return { "status" : 1 }
    
    def select_node(self, id):
        if id == 0:
            return { "status": 0 }
        node = models.Paste.get_by_id(id)
        return {
                "status": 1,
                "title": node.name,
                "mimetype": node.mimetype,
                "restricted": 1 if node.admin_only else 0,
                "text": node.code,
                "path": node.get_path()
                }
        
    def post(self):
        method = self.request.get('operation')
        id = self.request.get("id")
        ref = self.request.get("ref")
        copy = (self.request.get("copy") and self.request.get("copy") == "1")
        title = self.request.get("title")
        mimetype = self.request.get("mimetype")
        restricted = self.request.get("restricted")
        text = self.request.get("text")
        open = self.request.get("open")
        
        self.response.headers['Content-Type'] = 'text/json'
        if id == 0:
            result = { "status" : 0 } 
        elif method == "move_node":
            result = self.move_node(int(id), int(ref), copy)
        elif method == "update_node":
            result = self.update_node(int(id), title, mimetype, restricted, text, open)
        elif method == 'create_node':
            result = self.create_node(int(ref), title)
        elif method == "remove_node":
            result = self.remove_node(int(id))
        elif method == "select_node":
            result = self.select_node(int(id))
        else:
            result = { "status": 0, "error": "unknown method '{}'".format(method)}
            
        self.response.out.write(json.dumps(result))
            
        
        
        
class TreeViewHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/html'
        template_values = {
        }

        template = jinja_environment.get_template('paste_tree.html')
        self.response.out.write(template.render(template_values))
        
        
tree_view_handler = webapp2.WSGIApplication([('/tree/view', TreeViewHandler)], debug=True)
tree_data_handler = webapp2.WSGIApplication([('/tree/data', TreeDataHandler)], debug=True)
view_handler = webapp2.WSGIApplication([('/.*', PasteViewHandler)], debug=True)